#include <OpenCLTemplate.hpp>
#include <iostream>
#include <process.h>
#include <Windows.h>
#include <Timer.h>
#include <fstream>
using namespace std;

void gpu_run(void *args);
void cpu_run(void *args);

struct Matrix_dimen//for gpu
{
	unsigned int rows,rows2;
	unsigned int cols,cols2;
	PRECISION *input1,*input2;
	unsigned int id;		
	PRECISION *output;
	unsigned int no_of_threads;
	unsigned int Width, Width2, Width3, l;
	unsigned int gpu,cpu;//gpu
	int start_cpu, end_cpu, start_gpu, end_gpu;
} ;

struct Matrix_dimen1 ///for cpu
{
	unsigned int rows,rows2;
	unsigned int cols,cols2;
	PRECISION *input1,*input2;
	unsigned int id;		
	PRECISION *output;
	unsigned int no_of_threads;
	unsigned int Width, Width2, Width3, l;
	unsigned int cpu,gpu;//cpu
	int start_cpu, end_cpu, start_gpu, end_gpu;
} ;

Matrix_dimen A1;//gpu
Matrix_dimen1 A2;//cpu

int m_size;
int i;
ofstream myfile;
int main(int argc, char * argv[])
{
	//int m_size;
	cout<<"Enter Matrix Size"<<endl;
	cin>>m_size;

	
	myfile.open ("results.txt");	
	//int i=0;

	int platform1;
	cout<<"To perform CPU run Press 1"<<endl;
		cout<<"To perform GPU run Press 2"<<endl;
		cout<<"To perform CPU+GPU run Press 3"<<endl;
		cin>>platform1;//Set Platform by yourself here if you do not want to input it again and again and comment all the three lines above plus this line itself*/
		//platform1=1; //By setting the pltform1 1,2 or 3 will run the code in 10 iterations on a CPU, GPU or GPU+CPU in that order

	for(i=0; i<10; i++)
	{	
		Matrix_dimen B1;////B1 and A1 used for gpu, B2 and A2 used for cpu
		Matrix_dimen1 B2;//for cpu

	
		B1.rows =m_size*(i+1);
		B1.cols =m_size*(i+1);
		B1.rows2=m_size*(i+1);
		B1.cols2=m_size*(i+1);
		B1.Width  = B1.rows*B1.cols ; //we will assume that rows2*cols2 are also the same
		B1.Width2 = B1.rows2*B1.cols2;
		B1.Width3 = B1.rows*B1.cols2;
		B2.rows =m_size*(i+1);
		B2.cols =m_size*(i+1);
		B2.rows2=m_size*(i+1);
		B2.cols2=m_size*(i+1);
		B2.Width  = B2.rows*B2.cols ; //we will assume that rows2*cols2 are also the same
		B2.Width2 = B2.rows2*B2.cols2;
		B2.Width3 = B2.rows*B2.cols2;
		
		if (platform1==1)
		{
			B2.cpu=1;
			B2.gpu=0;
			B2.start_cpu=0;
			B2.end_cpu=m_size*(i+1);
			B2.start_gpu=0;
			B2.end_gpu=0;
			HANDLE *thread;
			thread = new HANDLE[1];
			thread[0]=(HANDLE)_beginthread(cpu_run, 0, (void*)&B2 );//like a cpu
			for (int i=0; i<1; i++)
			WaitForSingleObject(thread[i], INFINITE);
		}

		else if(platform1==2)
		{
			B1.gpu=1;
			B1.cpu=0;
			B1.start_gpu=0;
			B1.end_gpu=m_size*(i+1);
			B2.start_cpu=0;
			B2.end_cpu=0;
			HANDLE *thread;
			thread = new HANDLE[1];
			thread[0]=(HANDLE)_beginthread( gpu_run, 0, (void*)&B1 );//like a gpu
			for (int i=0; i<1; i++)
			WaitForSingleObject(thread[i], INFINITE);
		}

		else if(platform1==3)
		{
			//cout<<"Enter the percentage of work that you want the CPU to perform"<<endl;
			B1.start_gpu=floor((double)(m_size*(i+1))/32 * 0.15)*32;//gpu starting index 191 for 512 matrix, INCREMENT CAN ALSO BE SET TO 128
			B1.end_gpu=m_size*(i+1);//ending at m_size-1 as for loop set for i<rows
			B2.start_cpu=0;
			B2.end_cpu=B1.start_gpu;
			B1.gpu=1;
			B1.cpu=0;
			B2.cpu=1;//////chk it
			B2.gpu=0;
			HANDLE *thread;
			thread = new HANDLE[2];
			thread[0]=(HANDLE)_beginthread( gpu_run, 0, (void*)&B1 ); 
			thread[1]=(HANDLE)_beginthread( cpu_run, 0, (void*)&B2 );//like a cpu
			for (int i=0; i<2; i++)
			WaitForSingleObject(thread[i], INFINITE);
		}

		else
		{
			cout<<"Please select a proper option" << endl;
			return 1;
		}
		//myfile<<m_size*(i+1)<<"    "<<OpenCLTemplateSim.GetElapsedTime()<<endl;
}
	myfile.close();
	return 0;
}
void gpu_run(void *args)
{
	Matrix_dimen *A1 = (Matrix_dimen*)args;

	COpenCLTemplate OpenCLTemplateSim(/*Size=*/A1->rows,A1->cols,A1->rows2,A1->cols2,A1->Width,A1->Width2,A1->Width3,A1->l,A1->cpu, A1->gpu, A1->start_cpu, A1->end_cpu, A1->start_gpu, A1->end_gpu);//Working Combo 3
	// ================== Simulation ================
	OpenCLTemplateSim.StartTimer();
	OpenCLTemplateSim.CompleteRun(); // Complete GPU run.
	OpenCLTemplateSim.StopTimer();
	cout << "Total time taken = " << OpenCLTemplateSim.GetElapsedTime() << " seconds1." << endl;
	myfile<<m_size*(i+1)<<"    "<<OpenCLTemplateSim.GetElapsedTime()<<endl;
	
}

void cpu_run(void *args)
{
	Matrix_dimen1 *A2 = (Matrix_dimen1*)args;

	COpenCLTemplate OpenCLTemplateSim(/*Size=*/A2->rows,A2->cols,A2->rows2,A2->cols2,A2->Width,A2->Width2,A2->Width3,A2->l,A2->cpu, A2->gpu, A2->start_cpu, A2->end_cpu, A2->start_gpu, A2->end_gpu);//Working Combo 3
	// ================== Simulation ================
	OpenCLTemplateSim.StartTimer();
	OpenCLTemplateSim.CompleteRun(); // Complete GPU run.
	cout << "Total time taken = " << OpenCLTemplateSim.GetElapsedTime() << " seconds2." << endl;
	myfile<<m_size*(i+1)<<"    "<<OpenCLTemplateSim.GetElapsedTime()<<endl;
}

